// PART OF THE MACHINE SIMULATION. DO NOT CHANGE.

package nachos.machine;

import java.io.EOFException;

/**
 * COFF（通用对象文件格式）加载程序
 */
public class Coff {
    /**
     * 分配一个新的 Coff 对象
     */
    protected Coff() {
        file = null;
        entryPoint = 0;
        sections = null;
    }

    /**
     * 在指定的文件中加载 COFF 可执行文件
     *
     * <p>
     * 注意：
     * 如果构造函数成功返回，则该文件将成为此加载程序的属性，不应进一步访问
     * autograder 希望使用此加载器类。不要通过任何其他机构加载节段
     * 此加载程序将通过断言 read() 操作需要非零模拟时间来完成，从而验证文件是否由文件系统支持
     * 不要提供由模拟缓存支持的文件（此限制的主要目的是防止在处理页面错误时立即加载节）
     *
     * @param file 包含可执行文件的文件
     * @throws EOFException 如果可执行文件已损坏
     */
    public Coff(OpenFile file) throws EOFException {
        this.file = file;

        Coff coff = Machine.autoGrader().createLoader(file);

        if (coff != null) {
            this.entryPoint = coff.entryPoint;
            this.sections = coff.sections;
        } else {
            byte[] headers = new byte[headerLength + aoutHeaderLength];

            if (file.length() < headers.length) {
                Lib.debug(dbgCoff, "\tfile is not executable");
                throw new EOFException();
            }

            Lib.strictReadFile(file, 0, headers, 0, headers.length);

            int magic = Lib.bytesToUnsignedShort(headers, 0);
            int numSections = Lib.bytesToUnsignedShort(headers, 2);
            int optionalHeaderLength = Lib.bytesToUnsignedShort(headers, 16);
            int flags = Lib.bytesToUnsignedShort(headers, 18);
            entryPoint = Lib.bytesToInt(headers, headerLength + 16);

            if (magic != 0x0162) {
                Lib.debug(dbgCoff, "\tincorrect magic number");
                throw new EOFException();
            }
            if (numSections < 2 || numSections > 10) {
                Lib.debug(dbgCoff, "\tbad section count");
                throw new EOFException();
            }
            if ((flags & 0x0003) != 0x0003) {
                Lib.debug(dbgCoff, "\tbad header flags");
                throw new EOFException();
            }

            int offset = headerLength + optionalHeaderLength;

            sections = new CoffSection[numSections];
            for (int s = 0; s < numSections; s++) {
                int sectionEntryOffset = offset + s * CoffSection.headerLength;
                try {
                    sections[s] =
                            new CoffSection(file, this, sectionEntryOffset);
                } catch (EOFException e) {
                    Lib.debug(dbgCoff, "\terror loading section " + s);
                    throw e;
                }
            }
        }
    }

    /**
     * 返回可执行文件中的节数
     *
     * @return 可执行文件中的节数
     */
    public int getNumSections() {
        return sections.length;
    }

    /**
     * 返回可用于访问指定节的对象
     * 有效的节号包括 0 到 getNumSections() - 1
     *
     * @param sectionNumber 要选择的节
     * @return 可用于访问指定节的对象
     */
    public CoffSection getSection(int sectionNumber) {
        Lib.assertTrue(sectionNumber >= 0 && sectionNumber < sections.length);

        return sections[sectionNumber];
    }

    /**
     * 返回程序入口点
     * 这是运行程序前 PC 寄存器应初始化到的值
     *
     * @return 程序入口点
     */
    public int getEntryPoint() {
        Lib.assertTrue(file != null);

        return entryPoint;
    }

    /**
     * 关闭可执行文件并释放此加载程序分配的所有资源
     */
    public void close() {
        file.close();

        sections = null;
    }

    private OpenFile file;

    /**
     * 程序的第一条指令的虚拟地址
     */
    protected int entryPoint;
    /**
     * 此 COFF 可执行文件中的节
     */
    protected CoffSection sections[];

    private static final int headerLength = 20;
    private static final int aoutHeaderLength = 28;

    private static final char dbgCoff = 'c';
}
